﻿/*using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;

namespace ZOVNP.WinView.Utils
{
    class CustomBindingList<T> : BindingList<T>
    {
        protected override void RemoveItem(int index)
        {
            LastRemovedItem = this[index];
            base.RemoveItem(index);
        }

        public T LastRemovedItem { get; private set; }
    }
}
*/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
using System.Security;
using System.Security.Permissions;

namespace ZOVNP.WinView.Utils
{
    /// <summary>
    /// Provides a generic collection that supports data binding.
    /// </summary>
    /// <typeparam name="T">The type of elements in the list.</typeparam>
    [Serializable]
    [HostProtection(SecurityAction.LinkDemand, SharedState = true)]
    public class ExtendedBindingList<T> : Collection<T>, IBindingList, IList, ICollection, IEnumerable, ICancelAddNew, IRaiseItemChangedEvents
    {
        private int addNewPos = -1;
        private bool raiseListChangedEvents = true;
        [NonSerialized]
        private int lastChangeIndex = -1;
        private bool allowNew = true;
        private bool allowEdit = true;
        private bool allowRemove = true;
        private bool raiseItemChangedEvents;
        [NonSerialized]
        private PropertyDescriptorCollection itemTypeProperties;
        [NonSerialized]
        private PropertyChangedEventHandler propertyChangedEventHandler;
        [NonSerialized]
        private AddingNewEventHandler onAddingNew;
        [NonSerialized]
        private ListChangedEventHandler onListChanged;
        private bool userSetAllowNew;

        bool ItemTypeHasDefaultConstructor
        {
            get
            {
                Type type = typeof(T);
                return type.IsPrimitive || type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance, (Binder)null, new Type[0], (ParameterModifier[])null) != (ConstructorInfo)null;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether adding or removing items within the list raises <see cref="E:System.ComponentModel.BindingList`1.ListChanged"/> events.
        /// </summary>
        /// 
        /// <returns>
        /// true if adding or removing items raises <see cref="E:System.ComponentModel.BindingList`1.ListChanged"/> events; otherwise, false. The default is true.
        /// </returns>
        public bool RaiseListChangedEvents
        {
            get
            {
                return this.raiseListChangedEvents;
            }
            set
            {
                if (this.raiseListChangedEvents == value)
                    return;
                this.raiseListChangedEvents = value;
            }
        }

        bool AddingNewHandled
        {
            get
            {
                if (this.onAddingNew != null)
                    return this.onAddingNew.GetInvocationList().Length > 0;
                else
                    return false;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether you can add items to the list using the <see cref="M:System.ComponentModel.BindingList`1.AddNew"/> method.
        /// </summary>
        /// 
        /// <returns>
        /// true if you can add items to the list with the <see cref="M:System.ComponentModel.BindingList`1.AddNew"/> method; otherwise, false. The default depends on the underlying type contained in the list.
        /// </returns>
        public bool AllowNew
        {
            get
            {
                if (this.userSetAllowNew || this.allowNew)
                    return this.allowNew;
                else
                    return this.AddingNewHandled;
            }
            set
            {
                bool allowNew = this.AllowNew;
                this.userSetAllowNew = true;
                this.allowNew = value;
                if (allowNew == value)
                    return;
                this.FireListChanged(ListChangedType.Reset, -1);
            }
        }

        bool IBindingList.AllowNew
        {
            get
            {
                return this.AllowNew;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether items in the list can be edited.
        /// </summary>
        /// 
        /// <returns>
        /// true if list items can be edited; otherwise, false. The default is true.
        /// </returns>
        public bool AllowEdit
        {
            get
            {
                return this.allowEdit;
            }
            set
            {
                if (this.allowEdit == value)
                    return;
                this.allowEdit = value;
                this.FireListChanged(ListChangedType.Reset, -1);
            }
        }

        bool IBindingList.AllowEdit
        {
            get
            {
                return this.AllowEdit;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether you can remove items from the collection.
        /// </summary>
        /// 
        /// <returns>
        /// true if you can remove items from the list with the <see cref="M:System.ComponentModel.BindingList`1.RemoveItem(System.Int32)"/> method otherwise, false. The default is true.
        /// </returns>
        public bool AllowRemove
        {
            get
            {
                return this.allowRemove;
            }
            set
            {
                if (this.allowRemove == value)
                    return;
                this.allowRemove = value;
                this.FireListChanged(ListChangedType.Reset, -1);
            }
        }

        bool IBindingList.AllowRemove
        {
            get
            {
                return this.AllowRemove;
            }
        }

        bool IBindingList.SupportsChangeNotification
        {
            get
            {
                return this.SupportsChangeNotificationCore;
            }
        }

        /// <summary>
        /// Gets a value indicating whether <see cref="E:System.ComponentModel.BindingList`1.ListChanged"/> events are enabled.
        /// </summary>
        /// 
        /// <returns>
        /// true if <see cref="E:System.ComponentModel.BindingList`1.ListChanged"/> events are supported; otherwise, false. The default is true.
        /// </returns>
        protected virtual bool SupportsChangeNotificationCore
        {
            get
            {
                return true;
            }
        }

        bool IBindingList.SupportsSearching
        {
            get
            {
                return this.SupportsSearchingCore;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the list supports searching.
        /// </summary>
        /// 
        /// <returns>
        /// true if the list supports searching; otherwise, false. The default is false.
        /// </returns>
        protected virtual bool SupportsSearchingCore
        {
            get
            {
                return false;
            }
        }

        bool IBindingList.SupportsSorting
        {
            get
            {
                return this.SupportsSortingCore;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the list supports sorting.
        /// </summary>
        /// 
        /// <returns>
        /// true if the list supports sorting; otherwise, false. The default is false.
        /// </returns>
        protected virtual bool SupportsSortingCore
        {
            get
            {
                return false;
            }
        }

        bool IBindingList.IsSorted
        {
            get
            {
                return this.IsSortedCore;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the list is sorted.
        /// </summary>
        /// 
        /// <returns>
        /// true if the list is sorted; otherwise, false. The default is false.
        /// </returns>
        protected virtual bool IsSortedCore
        {
            get
            {
                return false;
            }
        }

        PropertyDescriptor IBindingList.SortProperty
        {
            get
            {
                return this.SortPropertyCore;
            }
        }

        /// <summary>
        /// Gets the property descriptor that is used for sorting the list if sorting is implemented in a derived class; otherwise, returns null.
        /// </summary>
        /// 
        /// <returns>
        /// The <see cref="T:System.ComponentModel.PropertyDescriptor"/> used for sorting the list.
        /// </returns>
        protected virtual PropertyDescriptor SortPropertyCore
        {
            get
            {
                return (PropertyDescriptor)null;
            }
        }

        ListSortDirection IBindingList.SortDirection
        {
            get
            {
                return this.SortDirectionCore;
            }
        }

        /// <summary>
        /// Gets the direction the list is sorted.
        /// </summary>
        /// 
        /// <returns>
        /// One of the <see cref="T:System.ComponentModel.ListSortDirection"/> values. The default is <see cref="F:System.ComponentModel.ListSortDirection.Ascending"/>.
        /// </returns>
        protected virtual ListSortDirection SortDirectionCore
        {
            get
            {
                return ListSortDirection.Ascending;
            }
        }

        bool IRaiseItemChangedEvents.RaisesItemChangedEvents
        {
            get
            {
                return this.raiseItemChangedEvents;
            }
        }

        /// <summary>
        /// Occurs before an item is added to the list.
        /// </summary>
        public event AddingNewEventHandler AddingNew
        {
            add
            {
                bool allowNew = this.AllowNew;
                this.onAddingNew += value;
                if (allowNew == this.AllowNew)
                    return;
                this.FireListChanged(ListChangedType.Reset, -1);
            }
            remove
            {
                bool allowNew = this.AllowNew;
                this.onAddingNew -= value;
                if (allowNew == this.AllowNew)
                    return;
                this.FireListChanged(ListChangedType.Reset, -1);
            }
        }

        /// <summary>
        /// Occurs when the list or an item in the list changes.
        /// </summary>
        public event ListChangedEventHandler ListChanged
        {
            add
            {
                this.onListChanged += value;
            }
            remove
            {
                this.onListChanged -= value;
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:System.ComponentModel.BindingList`1"/> class using default values.
        /// </summary>
        public ExtendedBindingList()
        {
            this.Initialize();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:System.ComponentModel.BindingList`1"/> class with the specified list.
        /// </summary>
        /// <param name="list">An <see cref="T:System.Collections.Generic.IList`1"/> of items to be contained in the <see cref="T:System.ComponentModel.BindingList`1"/>.</param>
        public ExtendedBindingList(IList<T> list)
            : base(list)
        {
            this.Initialize();
        }

        private void Initialize()
        {
            this.allowNew = this.ItemTypeHasDefaultConstructor;
            if (!typeof(INotifyPropertyChanged).IsAssignableFrom(typeof(T)))
                return;
            this.raiseItemChangedEvents = true;
            foreach (T obj in (IEnumerable<T>)this.Items)
                this.HookPropertyChanged(obj);
        }

        /// <summary>
        /// Raises the <see cref="E:System.ComponentModel.BindingList`1.AddingNew"/> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.ComponentModel.AddingNewEventArgs"/> that contains the event data. </param>
        protected virtual void OnAddingNew(AddingNewEventArgs e)
        {
            if (this.onAddingNew == null)
                return;
            this.onAddingNew((object)this, e);
        }

        private object FireAddingNew()
        {
            AddingNewEventArgs e = new AddingNewEventArgs((object)null);
            this.OnAddingNew(e);
            return e.NewObject;
        }

        /// <summary>
        /// Raises the <see cref="E:System.ComponentModel.BindingList`1.ListChanged"/> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.ComponentModel.ListChangedEventArgs"/> that contains the event data. </param>
        protected virtual void OnListChanged(ListChangedEventArgs e)
        {
            if (this.onListChanged == null)
                return;
            this.onListChanged((object)this, e);
        }

        /// <summary>
        /// Raises a <see cref="E:System.ComponentModel.BindingList`1.ListChanged"/> event of type <see cref="F:System.ComponentModel.ListChangedType.Reset"/>.
        /// </summary>
        public void ResetBindings()
        {
            this.FireListChanged(ListChangedType.Reset, -1);
        }

        /// <summary>
        /// Raises a <see cref="E:System.ComponentModel.BindingList`1.ListChanged"/> event of type <see cref="F:System.ComponentModel.ListChangedType.ItemChanged"/> for the item at the specified position.
        /// </summary>
        /// <param name="position">A zero-based index of the item to be reset.</param>
        public void ResetItem(int position)
        {
            this.FireListChanged(ListChangedType.ItemChanged, position);
        }

        private void FireListChanged(ListChangedType type, int index)
        {
            if (!this.raiseListChangedEvents)
                return;
            this.OnListChanged(new ListChangedEventArgs(type, index));
        }

        private void FireListChanged(ListChangedType type, int index, T item)
        {
            if (!this.raiseListChangedEvents)
                return;
            this.OnListChanged(new ExtendedListChangedEventArgs<T>(type, item, index));
        }

        /// <summary>
        /// Removes all elements from the collection.
        /// </summary>
        protected override void ClearItems()
        {
            this.EndNew(this.addNewPos);
            if (this.raiseItemChangedEvents)
            {
                foreach (T obj in (IEnumerable<T>)this.Items)
                    this.UnhookPropertyChanged(obj);
            }
            base.ClearItems();
            this.FireListChanged(ListChangedType.Reset, -1);
        }

        /// <summary>
        /// Inserts the specified item in the list at the specified index.
        /// </summary>
        /// <param name="index">The zero-based index where the item is to be inserted.</param><param name="item">The item to insert in the list.</param>
        protected override void InsertItem(int index, T item)
        {
            this.EndNew(this.addNewPos);
            base.InsertItem(index, item);
            if (this.raiseItemChangedEvents)
                this.HookPropertyChanged(item);
            this.FireListChanged(ListChangedType.ItemAdded, index);
        }

        /// <summary>
        /// Removes the item at the specified index.
        /// </summary>
        /// <param name="index">The zero-based index of the item to remove. </param><exception cref="T:System.NotSupportedException">You are removing a newly added item and <see cref="P:System.ComponentModel.IBindingList.AllowRemove"/> is set to false. </exception>
        protected override void RemoveItem(int index)
        {
            if (!this.allowRemove && (this.addNewPos < 0 || this.addNewPos != index))
                throw new NotSupportedException();
            this.EndNew(this.addNewPos);
            T item = this[index];
            if (this.raiseItemChangedEvents)
                this.UnhookPropertyChanged(item);
            base.RemoveItem(index);
            this.FireListChanged(ListChangedType.ItemDeleted, index, item);
        }

        /// <summary>
        /// Replaces the item at the specified index with the specified item.
        /// </summary>
        /// <param name="index">The zero-based index of the item to replace.</param><param name="item">The new value for the item at the specified index. The value can be null for reference types.</param><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="index"/> is less than zero.-or-<paramref name="index"/> is greater than <see cref="P:System.Collections.ObjectModel.Collection`1.Count"/>.</exception>
        protected override void SetItem(int index, T item)
        {
            if (this.raiseItemChangedEvents)
                this.UnhookPropertyChanged(this[index]);
            base.SetItem(index, item);
            if (this.raiseItemChangedEvents)
                this.HookPropertyChanged(item);
            this.FireListChanged(ListChangedType.ItemChanged, index);
        }

        /// <summary>
        /// Discards a pending new item.
        /// </summary>
        /// <param name="itemIndex">The index of the of the new item to be added </param>
        public virtual void CancelNew(int itemIndex)
        {
            if (this.addNewPos < 0 || this.addNewPos != itemIndex)
                return;
            this.RemoveItem(this.addNewPos);
            this.addNewPos = -1;
        }

        /// <summary>
        /// Commits a pending new item to the collection.
        /// </summary>
        /// <param name="itemIndex">The index of the new item to be added.</param>
        public virtual void EndNew(int itemIndex)
        {
            if (this.addNewPos < 0 || this.addNewPos != itemIndex)
                return;
            this.addNewPos = -1;
        }

        /// <summary>
        /// Adds a new item to the collection.
        /// </summary>
        /// 
        /// <returns>
        /// The item added to the list.
        /// </returns>
        /// <exception cref="T:System.InvalidOperationException">The <see cref="P:System.Windows.Forms.BindingSource.AllowNew"/> property is set to false. -or-A public default constructor could not be found for the current item type.</exception>
        public T AddNew()
        {
            return (T)this.AddNew();
        }

        object IBindingList.AddNew()
        {
            object obj = this.AddNewCore();
            this.addNewPos = obj != null ? this.IndexOf((T)obj) : -1;
            return obj;
        }

        /// <summary>
        /// Adds a new item to the end of the collection.
        /// </summary>
        /// 
        /// <returns>
        /// The item that was added to the collection.
        /// </returns>
        /// <exception cref="T:System.InvalidCastException">The new item is not the same type as the objects contained in the <see cref="T:System.ComponentModel.BindingList`1"/>.</exception>
        protected virtual object AddNewCore()
        {
            object obj = this.FireAddingNew() ?? SecurityUtils.SecureCreateInstance(typeof(T));
            this.Add((T)obj);
            return obj;
        }

        void IBindingList.ApplySort(PropertyDescriptor prop, ListSortDirection direction)
        {
            this.ApplySortCore(prop, direction);
        }

        /// <summary>
        /// Sorts the items if overridden in a derived class; otherwise, throws a <see cref="T:System.NotSupportedException"/>.
        /// </summary>
        /// <param name="prop">A <see cref="T:System.ComponentModel.PropertyDescriptor"/> that specifies the property to sort on.</param><param name="direction">One of the <see cref="T:System.ComponentModel.ListSortDirection"/>  values.</param><exception cref="T:System.NotSupportedException">Method is not overridden in a derived class. </exception>
        protected virtual void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
        {
            throw new NotSupportedException();
        }

        void IBindingList.RemoveSort()
        {
            this.RemoveSortCore();
        }

        /// <summary>
        /// Removes any sort applied with <see cref="M:System.ComponentModel.BindingList`1.ApplySortCore(System.ComponentModel.PropertyDescriptor,System.ComponentModel.ListSortDirection)"/> if sorting is implemented in a derived class; otherwise, raises <see cref="T:System.NotSupportedException"/>.
        /// </summary>
        /// <exception cref="T:System.NotSupportedException">Method is not overridden in a derived class. </exception>
        protected virtual void RemoveSortCore()
        {
            throw new NotSupportedException();
        }

        int IBindingList.Find(PropertyDescriptor prop, object key)
        {
            return this.FindCore(prop, key);
        }

        /// <summary>
        /// Searches for the index of the item that has the specified property descriptor with the specified value, if searching is implemented in a derived class; otherwise, a <see cref="T:System.NotSupportedException"/>.
        /// </summary>
        /// 
        /// <returns>
        /// The zero-based index of the item that matches the property descriptor and contains the specified value.
        /// </returns>
        /// <param name="prop">The <see cref="T:System.ComponentModel.PropertyDescriptor"/> to search for.</param><param name="key">The value of <paramref name="property"/> to match.</param><exception cref="T:System.NotSupportedException"><see cref="M:System.ComponentModel.BindingList`1.FindCore(System.ComponentModel.PropertyDescriptor,System.Object)"/> is not overridden in a derived class.</exception>
        protected virtual int FindCore(PropertyDescriptor prop, object key)
        {
            throw new NotSupportedException();
        }

        void IBindingList.AddIndex(PropertyDescriptor prop)
        {
        }

        void IBindingList.RemoveIndex(PropertyDescriptor prop)
        {
        }

        private void HookPropertyChanged(T item)
        {
            INotifyPropertyChanged notifyPropertyChanged = (object)item as INotifyPropertyChanged;
            if (notifyPropertyChanged == null)
                return;
            if (this.propertyChangedEventHandler == null)
                this.propertyChangedEventHandler = new PropertyChangedEventHandler(this.Child_PropertyChanged);
            notifyPropertyChanged.PropertyChanged += this.propertyChangedEventHandler;
        }

        private void UnhookPropertyChanged(T item)
        {
            INotifyPropertyChanged notifyPropertyChanged = (object)item as INotifyPropertyChanged;
            if (notifyPropertyChanged == null || this.propertyChangedEventHandler == null)
                return;
            notifyPropertyChanged.PropertyChanged -= this.propertyChangedEventHandler;
        }

        private void Child_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (!this.RaiseListChangedEvents)
                return;
            if (sender != null && e != null)
            {
                if (!string.IsNullOrEmpty(e.PropertyName))
                {
                    T obj;
                    try
                    {
                        obj = (T)sender;
                    }
                    catch (InvalidCastException ex)
                    {
                        this.ResetBindings();
                        return;
                    }
                    int newIndex = this.lastChangeIndex;
                    if (newIndex < 0 || newIndex >= this.Count || !this[newIndex].Equals((object)obj))
                    {
                        newIndex = this.IndexOf(obj);
                        this.lastChangeIndex = newIndex;
                    }
                    if (newIndex == -1)
                    {
                        this.UnhookPropertyChanged(obj);
                        this.ResetBindings();
                        return;
                    }
                    else
                    {
                        if (this.itemTypeProperties == null)
                            this.itemTypeProperties = TypeDescriptor.GetProperties(typeof(T));
                        PropertyDescriptor propDesc = this.itemTypeProperties.Find(e.PropertyName, true);
                        this.OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, newIndex, propDesc));
                        return;
                    }
                }
            }
            this.ResetBindings();
        }
    }

    public class ExtendedListChangedEventArgs<T> : ListChangedEventArgs
    {
        public ExtendedListChangedEventArgs(ListChangedType listChangedType, T item, int newIndex) : base(listChangedType, newIndex)
        {
            Item = item;
        }

        public ExtendedListChangedEventArgs(ListChangedType listChangedType, T item, int newIndex, PropertyDescriptor propDesc) : base(listChangedType, newIndex, propDesc)
        {
            Item = item;
        }

        public ExtendedListChangedEventArgs(ListChangedType listChangedType, T item, PropertyDescriptor propDesc) : base(listChangedType, propDesc)
        {
            Item = item;
        }

        public ExtendedListChangedEventArgs(ListChangedType listChangedType, T item, int newIndex, int oldIndex) : base(listChangedType, newIndex, oldIndex)
        {
            Item = item;
        }

        public readonly T Item;
    }

    internal static class SecurityUtils
    {
        private static ReflectionPermission memberAccessPermission;
        private static ReflectionPermission restrictedMemberAccessPermission;

        static ReflectionPermission MemberAccessPermission
        {
            get
            {
                if (SecurityUtils.memberAccessPermission == null)
                    SecurityUtils.memberAccessPermission = new ReflectionPermission(ReflectionPermissionFlag.MemberAccess);
                return SecurityUtils.memberAccessPermission;
            }
        }

        static ReflectionPermission RestrictedMemberAccessPermission
        {
            get
            {
                if (SecurityUtils.restrictedMemberAccessPermission == null)
                    SecurityUtils.restrictedMemberAccessPermission = new ReflectionPermission(ReflectionPermissionFlag.RestrictedMemberAccess);
                return SecurityUtils.restrictedMemberAccessPermission;
            }
        }

        static SecurityUtils()
        {
        }

        private static void DemandReflectionAccess(Type type)
        {
            try
            {
                SecurityUtils.MemberAccessPermission.Demand();
            }
            catch (SecurityException ex)
            {
                SecurityUtils.DemandGrantSet(type.Assembly);
            }
        }

        [SecuritySafeCritical]
        private static void DemandGrantSet(Assembly assembly)
        {
            PermissionSet permissionSet = assembly.PermissionSet;
            permissionSet.AddPermission((IPermission)SecurityUtils.RestrictedMemberAccessPermission);
            permissionSet.Demand();
        }

        private static bool HasReflectionPermission(Type type)
        {
            try
            {
                SecurityUtils.DemandReflectionAccess(type);
                return true;
            }
            catch (SecurityException ex)
            {
            }
            return false;
        }

        internal static object SecureCreateInstance(Type type)
        {
            return SecurityUtils.SecureCreateInstance(type, (object[])null, false);
        }

        internal static object SecureCreateInstance(Type type, object[] args, bool allowNonPublic)
        {
            if (type == (Type)null)
                throw new ArgumentNullException("type");
            BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance;
            if (!type.IsVisible)
                SecurityUtils.DemandReflectionAccess(type);
            else if (allowNonPublic && !SecurityUtils.HasReflectionPermission(type))
                allowNonPublic = false;
            if (allowNonPublic)
                bindingAttr |= BindingFlags.NonPublic;
            return Activator.CreateInstance(type, bindingAttr, (Binder)null, args, (CultureInfo)null);
        }
    }
}
